# -*- coding: utf-8 -*-
import atexit
import sys
import ssl
import time

from pyVim.connect import Disconnect, SmartConnect
from pyVmomi import vim, vmodl

# vcenter信息
# vcenter_ip = "10.2.31.140"
# vcenter_port = "443"
# vcenter_user = "administrator@vsphere.local"
# vcenter_pwd = "*(Ow5AXgf{"
vcenter_ip = "172.16.100.57"
vcenter_port = "443"
vcenter_user = "administrator@vsphere.local"
vcenter_pwd = "Shsnc123!@#"

# 分配信息
esxi_host = "172.16.100.50"
create_cpu_cores = 2
create_memory_sizeGB = 4
create_store_sizeGB = 500


# 磁盘大小来显示不同的单位（例如字节、千字节、兆字节、吉字节等
def format_disk_size(bytes):
    if bytes < 1024:
        return f"{bytes} 字节"
    elif bytes < 1024 * 1024:
        kilobytes = bytes / 1024
        return f"{kilobytes:.2f} KB"
    elif bytes < 1024 * 1024 * 1024:
        megabytes = bytes / (1024 * 1024)
        return f"{megabytes:.2f} MB"
    elif bytes < 1024 * 1024 * 1024 * 1024:
        gigabytes = bytes / (1024 * 1024 * 1024)
        return f"{gigabytes:.2f} GB"
    else:
        terabytes = bytes / (1024 * 1024 * 1024 * 1024)
        return f"{terabytes:.2f} TB"


# 时间
def format_time(seconds):
    if seconds < 60:
        return f"{seconds}秒"
    elif seconds < 3600:
        minutes = seconds // 60
        return f"{minutes}分钟"
    elif seconds < 86400:
        hours = seconds // 3600
        return f"{hours}小时"
    else:
        days = seconds // 86400
        return f"{days}天"


# 根据 CPU 大小来显示不同的单位（例如赫兹、千赫兹、兆赫兹等）
def format_cpu_speed(hertz):
    if hertz < 1000:
        return f"{hertz} Hz"
    elif hertz < 1000000:
        kilohertz = hertz / 1000
        return f"{kilohertz:.2f} kHz"
    elif hertz < 1000000000:
        megahertz = hertz / 1000000
        return f"{megahertz:.2f} MHz"
    else:
        gigahertz = hertz / 1000000000
        return f"{gigahertz:.2f} GHz"


# 根据内存大小来显示不同的单位（例如字节、千字节、兆字节、吉字节等）
def format_memory_size(bytes):
    if bytes < 1024:
        return f"{bytes} 字节"
    elif bytes < 1024 * 1024:
        kilobytes = bytes / 1024
        return f"{kilobytes:.2f} KB"
    elif bytes < 1024 * 1024 * 1024:
        megabytes = bytes / (1024 * 1024)
        return f"{megabytes:.2f} MB"
    else:
        gigabytes = bytes / (1024 * 1024 * 1024)
        return f"{gigabytes:.2f} GB"

# 虚拟机开关机需要的一个方法
def WaitForTasks(service_instance, tasks):
    """
    Given the service instance si and tasks, it returns after all the tasks are complete
    :param service_instance:
    :param tasks:
    :return:
    """
    pc = service_instance.content.propertyCollector
    task_list = [str(task) for task in tasks]
    # Create filter
    obj_specs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task) for task in tasks]
    property_spec = vmodl.query.PropertyCollector.PropertySpec(type=vim.Task, pathSet=[], all=True)
    filter_spec = vmodl.query.PropertyCollector.FilterSpec()
    filter_spec.objectSet = obj_specs
    filter_spec.propSet = [property_spec]
    pcfilter = pc.CreateFilter(filter_spec, True)
    try:
        version, state = None, None
        # Loop looking for updates till the state moves to a completed state.
        while True:
            update = pc.WaitForUpdates(version)
            for filter_set in update.filterSet:
                for obj_set in filter_set.objectSet:
                    task = obj_set.obj
                    for change in obj_set.changeSet:
                        if change.name == 'info':
                            state = change.val.state
                        elif change.name == 'info.state':
                            state = change.val
                        else:
                            continue

            # Check if the state moved to a completed state.
            if state == vim.TaskInfo.State.success:
                return task.info.result
            elif state == vim.TaskInfo.State.error:
                raise task.info.error
    finally:
        if pcfilter:
            pcfilter.Destroy()



class vCenterManager():
    def __init__(self, vcenter_ip, vcenter_port, vcenter_user, vcenter_pwd):
        self.vcenter_ip = vcenter_ip
        self.vcenter_port = vcenter_port
        self.vcenter_user = vcenter_user
        self.vcenter_pwd = vcenter_pwd
        self.context = None
        self.si = None
        # 建立与vSphere的连接
        if hasattr(ssl, '_create_unverified_context'):
            self.context = ssl._create_unverified_context()
        self.si = SmartConnect(host=self.vcenter_ip, port=self.vcenter_port, user=self.vcenter_user,
                               pwd=self.vcenter_pwd,
                               sslContext=self.context)
        if self.si is None:
            print("请检查vc账号密码")
            sys.exit(1)

        atexit.register(Disconnect, self.si)

    # vcenter资源信息
    def get_vcenter_info(self):
        # 总存储容量
        datastore_capacity = 0
        # 总存储剩余
        datastore_free = 0
        # 主机信息列表
        host_info_list = []
        store_info_list = []
        # 异常宿主机
        host_error_list = []

        content = self.si.RetrieveContent()
        # 查询数据中心
        # for datacenter in content.rootFolder.childEntity:
        #    # 数据中心名称
        #    dc_name = datacenter.name
        # 获取所有宿主机对象
        host_view = content.viewManager.CreateContainerView(content.rootFolder, [vim.HostSystem], True)
        hosts = host_view.view
        for host in hosts:
            # 获取宿主机的硬件信息
            hardware = host.hardware
            # 获取宿主机状态
            connection_state = host.runtime.connectionState
            power_state = host.runtime.powerState
            # 是否是维护模式
            in_maintenance_mode = host.runtime.inMaintenanceMode
            if connection_state == 'connected' and power_state == 'poweredOn' and not in_maintenance_mode:
                # 获取 CPU 信息
                cpu_info = hardware.cpuInfo
                cpu_capacity = cpu_info.numCpuCores * cpu_info.hz / 1e9
                cpu_usage = host.summary.quickStats.overallCpuUsage / 1000.0
                cpu_available = cpu_capacity - cpu_usage
                # 获取 内存 信息
                memory_capacity = hardware.memorySize / (1024 ** 3)  # 将字节转换为 GB
                memory_usage = host.summary.quickStats.overallMemoryUsage / 1024.0  # GB
                memory_available = memory_capacity - memory_usage

                # 输出
                host_info_dict = {
                    "hostname": host.name,  # 宿主机名称
                    # "cpu_ghz": cpu_info.hz / 1000000,  # GHZ
                    # "cpu_package": cpu_info.numCpuPackages,  # CPU插槽
                    # "cpu_core": cpu_info.numCpuCores,  # cpu核心
                    # "cpu_thread": cpu_info.numCpuThreads,  # cpu线程
                    "cpu_total": cpu_info.numCpuCores * cpu_info.numCpuCores * cpu_info.numCpuThreads,  # 总核心数
                    # "cpu_capacity": cpu_capacity,  # GHz
                    # "cpu_usage": cpu_usage,
                    "cpu_available": cpu_available,
                    "memory_capacity": memory_capacity,
                    # "memory_usage": memory_usage,
                    "memory_available": memory_available
                }
                # 加入主机列表
                host_info_list.append(host_info_dict)
                print(
                    f"宿主机{host.name}: 总CPU={host_info_dict['cpu_total']:.2f}GHz, 可用CPU={host_info_dict['cpu_available']:.2f}GHz, 总内存={host_info_dict['memory_capacity']:.2f}GB, 可用内存={host_info_dict['memory_available']:.2f}GB")
                # 遍历所有数据存储
                for datastore in host.datastore:
                    store_capacity = datastore.summary.capacity
                    store_free = datastore.summary.freeSpace
                    store_usage = store_capacity - store_free
                    store_info_dict = {
                        "store_name": host.name,
                        "store_capacity": store_capacity / (1024 ** 4),
                        "store_free": store_free / (1024 ** 4),
                        "store_used": store_usage / (1024 ** 4)
                    }
                    # 加入存储列表
                    store_info_list.append(store_info_dict)
                    # 总存储量
                    datastore_capacity += store_capacity
                    datastore_free += store_free
            else:
                print("{}宿主机异常,加入异常列表".format(host.name))
                host_error_list.append(host.name)

        # 总存储使用
        datastore_used = datastore_capacity - datastore_free
        datastore_info_dict = {
            "datastore_capacity": datastore_capacity / (1024 ** 4),  # TB
            "datastore_free": datastore_free / (1024 ** 3),  # GB
            "datastore_used": datastore_used / (1024 ** 3),  # GB
        }

        print(
            f"数据中心总存储: {datastore_info_dict['datastore_capacity']:.2f}TB, 可用存储: {datastore_info_dict['datastore_free']:.2f}GB")
        # 返回主机信息、本地存储信息、总存储、异常宿主机
        return host_info_list, store_info_list, datastore_info_dict, host_error_list

    def vm_power_oc(self, vm_name, flag):
        """
        虚拟机开关
        :param vm_name: 虚拟机名称
        :param flag: on/off: 开启、关闭
        :return:
        """
        content = self.si.RetrieveContent()
        # 查找虚拟机
        vm = None
        container = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True)
        vms = container.view
        for v in vms:
            if v.name == vm_name:
                vm = v
                break
        if vm:
            # 检查虚拟机当前状态
            if vm.runtime.powerState == "poweredOff" and flag == "on":
                # 开启虚拟机
                task = vm.PowerOnVM_Task()
                WaitForTasks(self.si, [task])
                print(f"虚拟机 '{vm_name}' 已开机")
            elif vm.runtime.powerState == "poweredOn" and flag == "off":
                # 关闭虚拟机
                task = vm.PowerOffVM_Task()
                WaitForTasks(self.si, [task])
                print(f"虚拟机 '{vm_name}' 已关闭")
            else:
                print(f"指令错误或找不到虚拟机 '{vm_name}'")

    # 虚拟机删除
    def delete_vm(self, vm_name):
        content = self.si.RetrieveContent()
        container = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True)
        vms = container.view
        for vm in vms:
            if vm.name == vm_name:
                try:
                    if vm.runtime.powerState == "poweredOn":
                        # 如果虚拟机正在运行，首先关闭它
                        task = vm.PowerOff()
                        task_info = task.info
                        task_info.state = vim.TaskInfo.State.success
                        task_info.result = None
                        # print(f"等待虚拟机 {vm_name} 关机...")
                        WaitForTasks(self.si, [task])
                    # 删除虚拟机
                    task = vm.Destroy()
                    task_info = task.info
                    task_info.state = vim.TaskInfo.State.success
                    task_info.result = None
                    start_time = time.time()
                    while task_info.state != vim.TaskInfo.State.success:
                        if time.time() - start_time > 300:  # 设置超时时间，单位秒
                            # raise Exception(f"删除虚拟机 {vm_name} 超时")
                            print({'code': 404, 'message': '虚拟机 %s 已超时5分钟' % vm_name})
                        time.sleep(5)  # 每5秒检查一次任务状态
                        task_info = task.info
                    print({'code': 200, 'message': '虚拟机 %s 已成功删除' % vm_name})
                except Exception as e:
                    print({'code': 404, 'message': ' %s' % e})

    # 虚拟机简略信息
    def get_vm_template(self, vm_hostname):
        # 名称
        # IP
        # 操作系统
        # CPU
        # 内存
        # 状态
        # 所属物理机
        content = self.si.RetrieveContent()
        container = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True)
        vms = container.view
        for vm in vms:
            if vm.name == vm_hostname:
                print('VMname:', vm.name)
                print('IP :{}'.format(vm.guest.ipAddress))
                print('操作系统：{}'.format(vm.guest.guestFullName))
                print("CPU:{}".format(vm.config.hardware.numCPU))
                print("内存大小： {:.0f} GB".format(vm.config.hardware.memoryMB / (1024)))
                print('虚拟机状态：{}'.format(vm.runtime.powerState))
                #"poweredOn": 虚拟机已经开机并正在运行。"poweredOff": 虚拟机已经关机。"suspended": 虚拟机处于挂起状态
                print('关联物理机：{}'.format(vm.runtime.host.name))

    # 虚拟机详细信息
    def get_vm_details(si, vm_name):
        content = si.RetrieveContent()
        container = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True)
        vms = container.view

        total_disk_capacity_gb = 0
        total_disk_free_gb = 0
        disk_block = 0

        vm_information = {}
        disk_data = {}
        disk_data2 = []
        for vm in vms:
            if vm.name == vm_name:
                vm_state = vm.runtime.powerState
                if vm_state == "poweredOn":
                    vm_information['status'] = "开机"
                else:
                    vm_information['status'] = "关机"
                vm_information.update({
                    "name": vm.name,
                    "IP": vm.guest.ipAddress,
                    "system": vm.config.guestFullName,
                    "cpu核数": vm.config.hardware.numCPU,
                    "mem": format_memory_size(vm.config.hardware.memoryMB * 1024 * 1024),
                    "host": vm.runtime.host.name,
                    "cpu 使用": format_cpu_speed(vm.summary.quickStats.overallCpuUsage * 1000000),
                    "mem使用": format_memory_size(vm.summary.quickStats.guestMemoryUsage * 1024 * 1024),
                })
                # 磁盘类型
                for device in vm.config.hardware.device:
                    if isinstance(device, vim.vm.device.VirtualDisk):
                        disk_capacity_kb = device.capacityInKB
                        total_disk_capacity_gb += disk_capacity_kb
                        # 硬盘块数统计
                        disk_block += 1
                        vm_information['硬盘块'] = disk_block
                        vm_information['总储存'] = format_disk_size(total_disk_capacity_gb * 1024)

                        # 硬盘详细情况
                        if isinstance(device.backing, vim.vm.device.VirtualDisk.FlatVer2BackingInfo):
                            if device.backing.thinProvisioned == True:
                                disk_data2.append({"disk名称": device.deviceInfo.label,
                                                   'disk大小': format_disk_size(disk_capacity_kb * 1024),
                                                   "disk类型": "精简置备"})
                            else:
                                if device.backing.eagerlyScrub == None:
                                    disk_data2.append({"disk名称": device.deviceInfo.label,
                                                       'disk大小': format_disk_size(disk_capacity_kb * 1024),
                                                       "disk类型": "厚置备延迟置零"})
                                elif device.backing.eagerlyScrub == True:
                                    disk_data2.append({"disk名称": device.deviceInfo.label,
                                                       'disk大小': format_disk_size(disk_capacity_kb * 1024),
                                                       "disk类型": "厚置备快速置零"})
                vm_information['disk'] = disk_data2

        return vm_information

def resource_check(vcinfo):
    # 判断主机信息和存储
    vcinfo_host = vcinfo[0]
    # vcinfo_store=vcinfo[1]
    vcinfo_datastore = vcinfo[2]

    # 判断主机资源
    for host in vcinfo_host:
        if host["hostname"] == esxi_host:
            if create_cpu_cores < host["cpu_available"] and create_memory_sizeGB < host[
                "memory_available"] and create_store_sizeGB < vcinfo_datastore["datastore_free"]:
                print(
                    f"当前{esxi_host}剩余：{host['cpu_available']:.2f}核CPU，{host['memory_available']:.2f}GB内存，{vcinfo_datastore['datastore_free']:.2f}GB存储")
                print("ESXI资源满足虚机创建所需资源，虚机创建可继续")
            else:
                print("资源不足")


if __name__ == '__main__':
    vc = vCenterManager(vcenter_ip, vcenter_port, vcenter_user, vcenter_pwd)
    # 返回vc资源
    vcinfo = vc.get_vcenter_info()
    host_info_list = vcinfo[0]
    # store_info_list=vcinfo[1]
    datastore_info_dict = vcinfo[2]
    host_error_list = vcinfo[3]
    print(host_info_list)
    # print(store_info_list)
    print(datastore_info_dict)
    print(host_error_list)

    # resource_check(vcinfo)

    #vc.vm_power_oc("snc-jumpserver","on")

    vc.get_vm_template("snc-k8s-harbor")

